from __future__ import division


letter = {1000000000: 'G',
        1000000: 'M',
        1000: 'K'}


class abbreviate(object):
    def __mod__(self, n):
        tokens = []
        for item in n.split():
            try:
                item = int(item)
                for g in sorted(letter.keys())[::-1]:
                    if item >= g:
                        tokens.append('%d%s' % (int(round(item / g)), letter[g]))
                        break
                else:
                    tokens.append(item)
            except:
                tokens.append(item)
        return ' '.join(tokens)


class Table(object):
    MEAN = 'mean'
    ABBREVIATE = abbreviate()
    HLINE = 4
    LEFT, CENTER, RIGHT = range(3)

    def __init__(self, *header):
        self.rows = [header]
        self.formats = ['%s'] * len(header)
        self.justifications = [Table.CENTER for i in xrange(len(header))]
        self.paired = {}
        self.exp_caps = {}

    def replace_header(self, header):
        for i, value in enumerate(header):
            self.set_header(i, value)

    def set_header(self, index, title):
        if index > self.width():
            raise ValueError('trying to change header at bad (%d) index' % index)

        hdr = list(self.rows[0])
        hdr[index] = title
        self.rows[0] = tuple(hdr)

    def set_exponent_cap(self, index, cap):
        self.exp_caps[index] = cap

    def _do_list_op(self, list_op, *row):
        if len(row) == 1 and row[0] == Table.HLINE:
            list_op([])
        else:
            list_op(row)

    def add(self, *row):
        self._do_list_op(self.rows.append, *row)

    def insert(self, index, *row):
        # XXX untested
        self._do_list_op(lambda x: self.rows.insert(index, x), *row)

    def width(self):
        return len(self.rows[0])

    def row_len(self):
        return len(self.rows[0])

    def set_justification(self, index, justification):
        self.justifications[index] = justification

    def set_format(self, index, fmt):
        self.formats[index] = fmt

    def set_formats(self, *formats):
        self.formats = formats

    def _much_less_than(self, value):
        return '<< %s' % value

    def _scientific(self, index, fmt, value):
        fmtd = fmt.replace('f', 'e') % value
        toks = fmtd.split('e')
        if len(toks) > 1:
            if toks[1] == '+00' or (index in self.exp_caps and int(toks[1][1:])
                    > self.exp_caps[index]):

                decimal_places = int(fmt.split('f')[0].split('.')[1])
                value = fmt % (1. / (10 ** decimal_places))
                return self._much_less_than(value)
        return fmtd

    def add_pair(self, column1, title, length=2):
        self.paired[column1] = (length, title)

    def _format(self):
        formatted = []

        for i, row in enumerate(self.rows):
            if i == 0 or not self.formats:
                formats = ['%s'] * len(row)
            else:
                formats = self.formats

            f = []
            for j in xrange(min(len(formats), len(row))):
                fmt, item = formats[j], row[j]
                try:
                    fmtd = fmt % item

                    try:
                        # User may ask for ABBREVIATE on floats, which will
                        # cause the following to fail.

                        fm = float(fmtd)
                    except ValueError:
                        fm = None

                    if (isinstance(item, float) and fm is not None and abs(fm -
                        0.) < 1e-6):

                        fmtd = self._scientific(j, fmt, item)

                    f.append(fmtd)
                except TypeError:
                    f.append(unicode(item))

            formatted.append(f)

        return formatted

    def __str__(self):
        return self.render()

    def render(self):
        raise NotImplementedError()
